package com.coderbean.demo.configuration;


import javax.sql.DataSource;

import org.flowable.rest.service.api.RestResponseFactory;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.flowable.spring.boot.FlowableSecurityAutoConfiguration;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.flowable.ui.modeler.rest.app.EditorGroupsResource;
import org.flowable.ui.modeler.rest.app.EditorUsersResource;
import org.flowable.ui.modeler.rest.app.ModelResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

import com.fasterxml.jackson.databind.ObjectMapper;

import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;

@Configuration
@EnableConfigurationProperties({ FlowableModelerAppProperties.class })
@ComponentScan(basePackages = { "org.flowable.ui.modeler.repository", "org.flowable.ui.modeler.service",
		"org.flowable.ui.common.service", "org.flowable.ui.common.repository", "org.flowable.ui.common.tenant",
		"org.flowable.ui.modeler.rest.app", "org.flowable.rest.service.api",
		"org.flowable.ui.task.service.debugger" }, excludeFilters = {
				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteIdmService.class),
				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ModelResource.class),
				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorUsersResource.class),
				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorGroupsResource.class),
				@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = FlowableSecurityAutoConfiguration.class) })
public class FlowableConfiguration implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
	
	public static final Logger log = LoggerFactory.getLogger(FlowableConfiguration.class);
	
	@Value("${spring.datasource.url}")
	private String jdbcUrl;

	@Value("${spring.datasource.driver-class-name}")
	private String jdbcDriverClassName;

	@Value("${spring.datasource.username}")
	private String username;

	@Value("${spring.datasource.password}")
	private String password;

	@Override
	public void configure(SpringProcessEngineConfiguration engineConfiguration) {
		engineConfiguration.setJdbcUrl(jdbcUrl);
		engineConfiguration.setJdbcDriver(jdbcDriverClassName);
		engineConfiguration.setJdbcUsername(username);
		engineConfiguration.setJdbcPassword(password);
		engineConfiguration.setActivityFontName("\u5B8B\u4F53");
		engineConfiguration.setLabelFontName("\u5B8B\u4F53");
	}

	@Autowired
	protected ObjectMapper objectMapper;

	@ConditionalOnMissingBean
	@Bean
	public RestResponseFactory restResponseFactory() {
		return new RestResponseFactory(objectMapper);
	}

	@Bean
	public Liquibase liquibase(DataSource dataSource) {
		log.info("Configuring Liquibase");
		Liquibase liquibase = null;
		try {
			DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
			Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
			database.setDatabaseChangeLogTableName("ACT_DE_" + database.getDatabaseChangeLogTableName());
			database.setDatabaseChangeLogLockTableName("ACT_DE_" + database.getDatabaseChangeLogLockTableName());

			liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml",
					new ClassLoaderResourceAccessor(), database);
			liquibase.update("flowable");
			return liquibase;

		} catch (Exception e) {
			throw new InternalServerErrorException("Error creating liquibase database", e);
		} finally {
			closeDatabase(liquibase);
		}
	}

	private void closeDatabase(Liquibase liquibase) {
		if (liquibase != null) {
			Database database = liquibase.getDatabase();
			if (database != null) {
				try {
					database.close();
				} catch (DatabaseException e) {
					log.warn("Error closing database", e);
				}
			}
		}
	}

}
